# -*- coding: utf-8 -*-

import numpy as np
import logging
import timeit
from librosa.feature import zero_crossing_rate, mfcc, spectral_centroid, spectral_rolloff, spectral_bandwidth, rms #rms in librpsa 0.7.0, rmse in previous version

class FeatureEngineer:
    RATE = 44100   # All recordings in ESC are 44.1 kHz
    FRAME = 512    # Frame size in samples

    def __init__(self, label=None):
        # Necessary to re-use code for training and prediction
        if label is None:
            self.label = ''
        else:
            self.label = label

    def feature_engineer(self, audio_data):
        """
        使用 librosa.feature 提取特征。

        每个信号被切分为几帧，计算每帧的特征并取平均值（中位数）。
        numpy 数组被转换为具有指定列的数据框。

        :param audio_data：输入信号样本，频率为 44.1 kHz
        :return：numpy 数组 (numOfFeatures x numOfShortTermWindows)
        """

        # 零交叉率（Zero Crossing Rate）特征数组，它反映了音频信号在每个帧内过零点的次数，可用于描述音频信号的变化剧烈程度。
        zcr_feat = self.compute_librosa_features(audio_data=audio_data, feat_name='zero_crossing_rate')
        # 均方根能量（Root Mean Square Energy）特征数组，它体现了音频信号在每个帧内的能量大小。
        rmse_feat = self.compute_librosa_features(audio_data=audio_data, feat_name='rmse')
        # 梅尔频率倒谱系数（Mel - Frequency Cepstral Coefficients）特征数组，这是音频处理中非常关键的特征，能够捕捉音频的音色和频谱特征。
        mfcc_feat = self.compute_librosa_features(audio_data=audio_data, feat_name= 'mfcc')
        # 频谱质心（Spectral Centroid）特征数组，它表示音频频谱的 “重心” 位置，可用于描述音频的明亮度。
        spectral_centroid_feat = self.compute_librosa_features(audio_data=audio_data, feat_name='spectral_centroid')
        # 频谱滚降（Spectral Rolloff）特征数组，它定义了音频频谱中大部分能量所在的频率范围。
        spectral_rolloff_feat = self.compute_librosa_features(audio_data=audio_data, feat_name='spectral_rolloff')
        # 频谱带宽（Spectral Bandwidth）特征数组，它衡量了音频频谱的宽度。
        spectral_bandwidth_feat = self.compute_librosa_features(audio_data=audio_data, feat_name='spectral_bandwidth')

        # 把多个数组沿着指定的轴拼接成一个新的数组。在这个代码里，axis = 0 意味着沿着垂直方向拼接。
        # 将不同类型的音频特征拼接在一起，能够把音频的时域、频域等多方面信息整合到一个数组中，这样可以为后续的音频分类、识别等任务提供更全面的特征表示。
        concat_feat = np.concatenate((zcr_feat,
                                      rmse_feat,
                                      mfcc_feat,
                                      spectral_centroid_feat,
                                      spectral_rolloff_feat,
                                      spectral_bandwidth_feat
                                      ), axis=0)

        logging.info('Averaging...')
        start = timeit.default_timer()

        # 求平均值，能够得到每个特征的平均数值，从而用一个向量来表示整个音频信号的特征，方便后续的机器学习模型进行训练和预测。
        mean_feat = np.mean(concat_feat, axis=1, keepdims=True).transpose()

        stop = timeit.default_timer()
        logging.info('Time taken: {0}'.format(stop - start))

        return mean_feat, self.label

    def compute_librosa_features(self, audio_data, feat_name):
        """
        使用 librosa 方法计算特征

        :param audio_data: 信号
        :param feat_name: 待计算的特征
        :return: np 数组
        """
        # # http://stackoverflow.com/questions/41896123/librosa-feature-tonnetz-ends-up-in-typeerror
        # chroma_cens_feat = chroma_cens(y=audio_data, sr=self.RATE, hop_length=self.FRAME)

        logging.info('Computing {}...'.format(feat_name))

        if feat_name == 'zero_crossing_rate':
            return zero_crossing_rate(y=audio_data, hop_length=self.FRAME)
        elif feat_name == 'rmse':
            return rms(y=audio_data, hop_length=self.FRAME)
        elif feat_name == 'mfcc':
            return mfcc(y=audio_data, sr=self.RATE, n_mfcc=13)
        elif feat_name == 'spectral_centroid':
            return spectral_centroid(y=audio_data, sr=self.RATE, hop_length=self.FRAME)
        elif feat_name == 'spectral_rolloff':
            return spectral_rolloff(y=audio_data, sr=self.RATE, hop_length=self.FRAME, roll_percent=0.90)
        elif feat_name == 'spectral_bandwidth':
            return spectral_bandwidth(y=audio_data, sr=self.RATE, hop_length=self.FRAME)